﻿using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Drawing;
using System.Data;
using System.Linq;
using System.Text;
using System.Windows.Forms;
using nGSO.KnapsackProblem;
using System.Configuration;
using System.Reflection;
using nGSO;
using System.Threading;
using nGSO.UI;
using nGSO.Performance;
using nGSO.Model;

namespace nGSO.UI
{
    public partial class KPPanel : UserControl
    {
        KpConfigElement KPConf
        {
            get
            {
                return bindingKPConfig.Current as KpConfigElement;
            }
        }

        public KPPanel()
        {
            InitializeComponent();

            BindToKPConfig();
            
        }

        private void BindToKPConfig()
        {
            KPConfigSection kpConfSection = (KPConfigSection)ConfigurationManager.GetSection("kp");

            var configItems = kpConfSection.Instances.Cast<KpConfigElement>();

            bindingKPConfig.DataSource = configItems;

            KpConfigElement configItem = configItems.First();
            tblKPConfig.Controls.Clear();
            tblKPConfig.ColumnCount = 2;

            var props = (from p in configItem.GetType().GetProperties()
                         let attr = p.GetCustomAttributes(typeof(ConfigurationPropertyAttribute), true)
                         where attr.Length == 1
                         select p).ToArray();

            for (int i = 0; i < props.Length; i++)
            {
                PropertyInfo p = props[i];
                var attrs = p.Attributes;
                int row = AddAutoSizeRow();

                tblKPConfig.Controls.Add(new Label() { Text = p.Name, Name = "lbl" + p.Name , Width=150, Anchor = AnchorStyles.Bottom }, 0, i);
                TextBox tbx = new TextBox();
                tbx.Name = "tbx" + p.Name;
                
                string value = p.GetValue(configItem, null).ToString();
                if (value.Length > 5)
                {
                    tbx.Multiline = true;
                    tbx.Width = 500;
                    tbx.Height = 50;
                    tbx.Anchor = AnchorStyles.None;
                }
                Binding b = new Binding("Text", bindingKPConfig, p.Name, true);
                tbx.DataBindings.Add(b);
                tblKPConfig.Controls.Add(tbx, 1, i);
            }
        }

        public int AddAutoSizeRow()
        {
            tblKPConfig.RowStyles.Add(new RowStyle(SizeType.AutoSize));
            tblKPConfig.RowCount = tblKPConfig.RowStyles.Count;
            int mCurrentRow = tblKPConfig.RowCount - 1;
            return mCurrentRow;
        }

        private void btnStartKP_Click(object sender, EventArgs e)
        {
            if (!backgroundWorker1.IsBusy)
            {
                tbxKPResult.Clear();
                toolStripProgressBar1.Value = 0;
                toolStripProgressBar1.Maximum = KPConf.Iterations * KPConf.TestCount;
                lblProgress.Text = "Started!";

                backgroundWorker1.RunWorkerAsync();

                ToggleEnableConfigControls(false);
            }
        }

        private void ToggleEnableConfigControls(bool on)
        {
            Invoke(new Action(delegate()
            {
                for (int i = 0; i < tblKPConfig.Controls.Count; i++)
                {
                    tblKPConfig.Controls[i].Enabled = on;
                }
            }));
        }

        void engine_IterationCompleted(object sender, IterationProgressEventArgs e)
        {
            Invoke(new Action(delegate()
            {
                toolStripProgressBar1.Increment(1);
            }));
        }

        void InsertMsg(string msg)
        {
            Invoke(new Action(delegate() 
                {
                    tbxKPResult.Text += DateTime.Now + " " + msg + System.Environment.NewLine;    
                }));
        }

        private void backgroundWorker1_DoWork(object sender, DoWorkEventArgs e)
        {
            InsertMsg("Start KP");

            KpConfigElement c = KPConf;

            GsoConfig config = new GsoConfig(c.UpdateDecisionRange, c.LuceferinInitialValue, c.DissipationOfLuceferin, c.Agents,
                c.DecisionRange, c.Iterations, c.NeighbourhoodThreshold, c.UpdateLuceferingValue, c.StepSize, 1, new MaximumProbabilitySelector());

            PerfManager perf = new PerfManager();
            config.SelectedOptimizationTask = new Config.OptimizationTaskElement() { Name = "KP Problem_" + c.TestSetName };
            PerfTests test = perf.StartTest(config);

            for (int i = 0; i < KPConf.TestCount; i++)
            {
                InsertMsg("Begin test " + i + " - " + KPConf.TestCount);

                GsoConfig.FitnessFunPointer = new KPFitnessCalulator(KPConf);
                KPSearchSpace space = new KPSearchSpace(config, KPConf);
                GsoEngine engine = new GsoEngine(space, new DefaultNeighboursLocalizator(), config);
                
                engine.IterationCompleted += new IterationCompletedHandler(engine_IterationCompleted);

                if (backgroundWorker1.CancellationPending)
                {
                    engine.Stop();
                    InsertMsg("KP optimization cancelled");
                    ToggleEnableConfigControls(true);
                    return;
                }
                
                var gsoResult = engine.StartGSOSolving();

                perf.AddKpResult(test, gsoResult, KPConf, i);
            }

            perf.UpdateOverallKPEffcienty(test, KPConf);
            InsertMsg("Results saved to database");

            ToggleEnableConfigControls(true);
        }

        private void backgroundWorker1_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
        {
            if (e.Cancelled)
            {
                lblProgress.Text = "Canceled!";
            }
            else
            {
                lblProgress.Text = "Completed!";
             
            }
            toolStripProgressBar1.ProgressBar.Value = 0;
        }


        private void btnStop_Click(object sender, EventArgs e)
        {
            backgroundWorker1.CancelAsync();
            ToggleEnableConfigControls(true);
            lblProgress.Text = "Stopped";
        }

        private void backgroundWorker1_ProgressChanged(object sender, ProgressChangedEventArgs e)
        {

        }
        
    }
}
